<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>股票数据管理系统</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-ui@2.15.14/lib/theme-chalk/index.css">
  <script src="https://cdn.jsdelivr.net/npm/element-ui@2.15.14/lib/index.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js"></script>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: 'Helvetica Neue', Arial, sans-serif;
    }

    body {
      background-color: #f5f7fa;
      color: #333;
      line-height: 1.6;
    }

    #app {
      max-width: 1600px;
      margin: 0 auto;
      padding: 20px;
    }

    .app-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 15px 20px;
      background: linear-gradient(135deg, #1a3a8f 0%, #0d1b4d 100%);
      color: white;
      border-radius: 10px;
      margin-bottom: 20px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    }

    .logo-section {
      display: flex;
      align-items: center;
    }

    .logo {
      width: 50px;
      height: 50px;
      background-color: #fff;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      margin-right: 15px;
      font-size: 24px;
      font-weight: bold;
      color: #1a3a8f;
    }

    .app-title {
      font-size: 24px;
      font-weight: bold;
    }

    .app-subtitle {
      font-size: 14px;
      opacity: 0.8;
    }

    .user-section {
      display: flex;
      align-items: center;
    }

    .user-info {
      text-align: right;
      margin-right: 15px;
    }

    .user-name {
      font-weight: bold;
      font-size: 16px;
    }

    .user-role {
      font-size: 13px;
      opacity: 0.8;
    }

    .user-avatar {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      background-color: #4a6fcb;
      display: flex;
      align-items: center;
      justify-content: center;
      color: white;
      font-weight: bold;
    }

    .main-container {
      display: grid;
      grid-template-columns: 250px 1fr;
      gap: 20px;
    }

    .sidebar {
      background: white;
      border-radius: 10px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
      padding: 20px 0;
    }

    .menu-item {
      padding: 14px 25px;
      display: flex;
      align-items: center;
      cursor: pointer;
      transition: all 0.3s;
      color: #555;
      font-weight: 500;
    }

    .menu-item:hover {
      background-color: #f0f5ff;
      color: #1a3a8f;
    }

    .menu-item.active {
      background-color: #e6f0ff;
      color: #1a3a8f;
      border-left: 4px solid #1a3a8f;
    }

    .menu-icon {
      margin-right: 12px;
      font-size: 18px;
    }

    .content-area {
      display: flex;
      flex-direction: column;
      gap: 20px;
    }

    .dashboard-cards {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
      gap: 20px;
      margin-bottom: 20px;
    }

    .card {
      background: white;
      border-radius: 10px;
      padding: 20px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
    }

    .card-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 15px;
    }

    .card-title {
      font-size: 16px;
      font-weight: bold;
      color: #1a3a8f;
    }

    .card-value {
      font-size: 28px;
      font-weight: bold;
      margin: 10px 0;
    }

    .card-footer {
      display: flex;
      align-items: center;
      color: #666;
      font-size: 14px;
    }

    .trend-up {
      color: #e74c3c;
      font-weight: bold;
      margin-left: 5px;
    }

    .trend-down {
      color: #2ecc71;
      font-weight: bold;
      margin-left: 5px;
    }

    .chart-container {
      height: 300px;
      margin-top: 10px;
    }

    .data-section {
      background: white;
      border-radius: 10px;
      padding: 25px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
    }

    .section-title {
      font-size: 20px;
      font-weight: bold;
      margin-bottom: 20px;
      color: #1a3a8f;
      display: flex;
      align-items: center;
    }

    .section-title i {
      margin-right: 10px;
    }

    .filter-bar {
      display: flex;
      flex-wrap: wrap;
      gap: 15px;
      margin-bottom: 20px;
      padding: 15px;
      background-color: #f8fafd;
      border-radius: 8px;
    }

    .filter-item {
      display: flex;
      align-items: center;
    }

    .filter-label {
      margin-right: 10px;
      font-weight: 500;
      color: #555;
    }

    .action-bar {
      display: flex;
      gap: 12px;
      margin-bottom: 20px;
      flex-wrap: wrap;
    }

    .el-button--stock {
      background: linear-gradient(135deg, #1a3a8f 0%, #0d1b4d 100%);
      color: white;
      border: none;
    }

    .el-button--stock:hover {
      opacity: 0.9;
    }

    .price-up {
      color: #e74c3c;
      font-weight: bold;
    }

    .price-down {
      color: #2ecc71;
      font-weight: bold;
    }

    .stats-result {
      margin-top: 20px;
      padding: 15px;
      background-color: #f8fafd;
      border-radius: 8px;
      font-size: 16px;
    }

    .stats-value {
      font-weight: bold;
      color: #1a3a8f;
      font-size: 20px;
      margin-top: 5px;
    }

    .pagination-container {
      display: flex;
      justify-content: flex-end;
      margin-top: 20px;
    }

    .footer {
      text-align: center;
      padding: 20px;
      color: #777;
      font-size: 14px;
      margin-top: 30px;
      border-top: 1px solid #eee;
    }

    @media (max-width: 992px) {
      .main-container {
        grid-template-columns: 1fr;
      }

      .sidebar {
        display: none;
      }
    }

    @media (max-width: 768px) {
      .dashboard-cards {
        grid-template-columns: 1fr;
      }

      .app-header {
        flex-direction: column;
        text-align: center;
        padding: 15px;
      }

      .user-section {
        margin-top: 15px;
      }

      .user-info {
        text-align: center;
      }
    }
  </style>
</head>
<body>
  {% raw %}
  <div id="app" v-cloak>
    <!-- 顶部标题栏 -->
    <header class="app-header">
      <div class="logo-section">
        <div class="logo">$</div>
        <div>
          <div class="app-title">股票数据管理系统</div>
          <div class="app-subtitle">专业金融数据分析平台</div>
        </div>
      </div>
      <div class="user-section">
        <div class="user-info">
          <div class="user-name">管理员</div>
          <div class="user-role">系统管理员</div>
        </div>
        <div class="user-avatar">A</div>
      </div>
    </header>

    <div class="main-container">
      <!-- 侧边栏菜单 -->
      <div class="sidebar">
        <div
          v-for="item in menuItems"
          :key="item.index"
          class="menu-item"
          :class="{ active: activeIndex === item.index }"
          @click="handleSelect(item.index)"
        >
          <i class="menu-icon" :class="item.icon"></i>
          <span>[[ item.title ]]</span>
        </div>
      </div>

      <!-- 主内容区 -->
      <div class="content-area">
        <!-- 数据概览卡片 -->
        <div class="dashboard-cards">
          <div class="card">
            <div class="card-header">
              <div class="card-title">股票总数</div>
              <i class="el-icon-s-marketing" style="color: #1a3a8f; font-size: 24px;"></i>
            </div>
            <div class="card-value">[[ totalStockCount.toLocaleString() ]]</div>
          </div>

          <div class="card">
            <div class="card-header">
              <div class="card-title">上涨股票数</div>
              <i class="el-icon-top" style="color: #e74c3c; font-size: 24px;"></i>
            </div>
            <div class="card-value price-up">[[ risingStocks.toLocaleString() ]]</div>
          </div>

          <div class="card">
            <div class="card-header">
              <div class="card-title">下跌股票数</div>
              <i class="el-icon-bottom" style="color: #2ecc71; font-size: 24px;"></i>
            </div>
            <div class="card-value price-down">[[ fallingStocks.toLocaleString() ]]</div>
          </div>

          <div class="card">
            <div class="card-header">
              <div class="card-title">平均股价</div>
              <i class="el-icon-s-finance" style="color: #1a3a8f; font-size: 24px;"></i>
            </div>
            <div class="card-value">¥[[ averagePrice.toFixed(2) ]]</div>
          </div>
        </div>

        <!-- 股票涨跌幅分布图表 -->
        <div class="card">
          <div class="card-header">
            <div class="card-title">股票涨跌幅百分比分布（基于全部数据）</div>
            <div>
              <el-radio-group v-model="chartType" size="mini">
                <el-radio-button label="day">日线</el-radio-button>
                <el-radio-button label="week">周线</el-radio-button>
                <el-radio-button label="month">月线</el-radio-button>
              </el-radio-group>
            </div>
          </div>
          <div id="stockChart" class="chart-container"></div>
        </div>

        <!-- 数据管理区域 -->
        <div class="data-section">
          <div class="section-title">
            <i class="el-icon-s-management"></i> 数据管理
          </div>

          <!-- 过滤和操作栏 -->
          <div class="filter-bar">
            <div class="filter-item">
              <span class="filter-label">数据表:</span>
              <el-select v-model="currentTable" placeholder="请选择数据表" @change="onTableChange" style="width: 200px;">
                <el-option
                  v-for="table in tables"
                  :key="table"
                  :label="table"
                  :value="table"
                ></el-option>
              </el-select>
            </div>

            <div class="filter-item">
              <span class="filter-label">搜索:</span>
              <el-input
                v-model="searchQuery"
                placeholder="输入搜索内容"
                @keyup.enter.native="searchData"
                style="width: 250px;"
              >
                <el-button slot="append" icon="el-icon-search" @click="searchData"></el-button>
              </el-input>
            </div>

            <div class="filter-item">
              <span class="filter-label">排序:</span>
              <el-input
                v-model="sortField"
                placeholder="字段名"
                size="small"
                style="width: 120px;"
              ></el-input>
              <el-select v-model="sortOrder" placeholder="排序" size="small" style="width: 100px; margin-left: 5px;">
                <el-option label="升序" value="asc"></el-option>
                <el-option label="降序" value="desc"></el-option>
              </el-select>
              <el-button
                type="primary"
                size="small"
                @click="sortTable"
                style="margin-left: 5px;"
              >
                <i class="el-icon-sort"></i>
              </el-button>
            </div>
          </div>

          <!-- 操作按钮组 -->
          <div class="action-bar">
            <el-button type="danger" plain @click="deleteSelected" :disabled="!selectedRows.length">
              <i class="el-icon-delete"></i> 批量删除
            </el-button>
            <el-button type="primary" class="el-button--stock" @click="showEditDialog">
              <i class="el-icon-plus"></i> 新增记录
            </el-button>
            <el-button type="success" plain @click="showStatsDialog">
              <i class="el-icon-s-data"></i> 数据统计
            </el-button>
            <el-button type="warning" plain @click="exportData">
              <i class="el-icon-download"></i> 导出数据
            </el-button>
          </div>

          <!-- 数据表格 -->
          <div class="table-container">
            <el-table
              :data="tableData"
              style="width: 100%"
              @selection-change="handleSelectionChange"
              stripe
              v-loading="loading"
              border
            >
              <el-table-column type="selection" width="55" align="center"></el-table-column>
              <el-table-column type="index" width="60" label="序号" align="center"></el-table-column>

              <el-table-column
                v-for="field in tableFields"
                :key="field.Field"
                :prop="field.Field"
                :label="field.Field"
                align="center"
                :show-overflow-tooltip="true"
                min-width="120"
              >
                <template slot-scope="scope">
                  <span v-if="currentTable === 'stock_data' && (field.Field === 'price_change' || field.Field === 'change_percentage')"
                        v-html="formatPriceField(scope.row, field.Field)">
                  </span>
                  <span v-else v-text="scope.row[field.Field] || '-'">
                  </span>
                </template>
              </el-table-column>

              <el-table-column align="center" label="操作" width="140" fixed="right">
                <template slot-scope="scope">
                  <el-button type="primary" size="mini" @click="editRecord(scope.row)">
                    <i class="el-icon-edit"></i> 编辑
                  </el-button>
                  <el-button type="danger" size="mini" @click="deleteRecord(scope.row)">
                    <i class="el-icon-delete"></i> 删除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>

            <!-- 空状态 -->
            <div v-if="!tableData.length" class="empty-state" style="margin: 40px 0; text-align: center;">
              <el-empty description="暂无数据"></el-empty>
            </div>
          </div>

          <!-- 分页 -->
          <div class="pagination-container" v-if="tableData.length">
            <el-pagination
              @size-change="handlePageSizeChange"
              @current-change="handleCurrentPageChange"
              :current-page="currentPage"
              :page-sizes="[10, 20, 50, 100]"
              :page-size="pageSize"
              layout="total, sizes, prev, pager, next, jumper"
              :total="totalCount"
            ></el-pagination>
          </div>
        </div>
      </div>
    </div>

    <!-- 编辑对话框 -->
    <el-dialog
      :title="currentRecord.id ? '编辑记录' : '新增记录'"
      :visible.sync="editDialogVisible"
      width="50%"
      center
    >
      <el-form ref="editForm" :model="currentRecord" label-width="120px" :rules="editRules" class="dialog-form">
        <el-form-item v-for="field in tableFields" :key="field.Field" :label="field.Field" :prop="field.Field" v-if="field.Field !== 'id'">
          <el-input v-model="currentRecord[field.Field]" placeholder="输入内容"></el-input>
        </el-form-item>
        <el-form-item style="text-align: center; margin-top: 30px;">
          <el-button @click="editDialogVisible = false" style="width: 100px;">取消</el-button>
          <el-button type="primary" @click="saveRecord" style="width: 100px;">提交</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>

    <!-- 统计对话框 -->
    <el-dialog
      title="数据统计"
      :visible.sync="statsDialogVisible"
      width="40%"
      center
    >
      <el-form :inline="true" class="demo-form-inline">
        <el-form-item label="选择字段" style="margin-right: 20px;">
          <el-select v-model="statsField" placeholder="请选择字段" style="width: 180px;">
            <el-option
              v-for="field in tableFields"
              :key="field.Field"
              :label="field.Field"
              :value="field.Field"
            ></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="统计类型">
          <el-select v-model="statsType" placeholder="请选择类型" style="width: 150px;">
            <el-option label="平均值" value="avg"></el-option>
            <el-option label="中位数" value="median"></el-option>
            <el-option label="众数" value="mode"></el-option>
            <el-option label="极差" value="range"></el-option>
            <el-option label="标准差" value="stddev"></el-option>
          </el-select>
        </el-form-item>
      </el-form>

      <div style="text-align: center; margin: 20px 0;">
        <el-button type="primary" @click="calculateStats" style="width: 150px;">计算</el-button>
      </div>

      <div v-if="statsResult" class="stats-result">
        <div>统计字段：<strong>[[ statsField ]]</strong></div>
        <div>统计方法：<strong v-text="getStatsTypeName(statsType)"></strong></div>
        <div>计算结果：</div>
        <div class="stats-value">[[ statsResult ]]</div>
      </div>
    </el-dialog>

    <!-- 页脚 -->
    <div class="footer">
      © 2023 股票数据管理系统 | 专业金融数据分析平台
    </div>
  </div>
  {% endraw %}

  <script>
    document.addEventListener('DOMContentLoaded', function() {
      new Vue({
        el: "#app",
        delimiters: ['[[', ']]'],
        data() {
          return {
            menuItems: [
              { index: '1', title: '数据展示', icon: 'el-icon-s-home' },
              { index: '2', title: '美股分析（部分）', icon: 'el-icon-s-flag' },
              { index: '3', title: '沪深京股分析', icon: 'el-icon-s-marketing' },
              { index: '4', title: '美股分析', icon: 'el-icon-s-marketing' },
              { index: '5', title: '港股分析', icon: 'el-icon-s-marketing' },
              { index: '6', title: '英股分析', icon: 'el-icon-s-marketing' },
              { index: '7', title: '数据爬取', icon: 'el-icon-download' }
            ],
            activeIndex: '1',
            tables: [],
            currentTable: "stock_data", // 默认表
            tableFields: [],
            tableData: [],
            allStockData: [], // 存储全部股票数据
            searchQuery: "",
            currentPage: 1,
            pageSize: 10,
            totalCount: 0,
            selectedRows: [],
            editDialogVisible: false,
            currentRecord: {},
            sortField: '',
            sortOrder: 'asc',
            statsDialogVisible: false,
            statsField: '',
            statsType: 'avg',
            statsResult: null,
            loading: false,
            editRules: {},
            chartType: 'day',
            totalStockCount: 0,
            risingStocks: 0,
            fallingStocks: 0,
            averagePrice: 0
          }
        },
        mounted() {
          this.initApp();
          this.renderStockChart();
        },
        watch: {
          chartType() {
            this.renderStockChart();
          },
          currentTable() {
            if (this.currentTable) {
              this.onTableChange();
            }
          }
        },
        methods: {
          initApp() {
            this.loading = true;
            this.fetchTables().catch(error => {
              console.error('应用初始化失败', error);
              this.$message.error('数据加载失败，请刷新页面');
            }).finally(() => {
              this.loading = false;
            });
          },

          fetchTables() {
            return axios.get('http://127.0.0.1:8080/tables')
              .then(response => {
                this.tables = response.data;
                if (this.tables.length) {
                  if (!this.currentTable || !this.tables.includes(this.currentTable)) {
                    this.currentTable = this.tables[0];
                  }
                  return this.onTableChange();
                }
              });
          },

          onTableChange() {
            if (!this.currentTable) return;
            this.loading = true;
            axios.get(`http://127.0.0.1:8080/${this.currentTable}/structure`)
              .then(response => {
                this.tableFields = response.data;
                this.generateEditRules();
                this.fetchData();
              })
              .catch(error => {
                console.error('获取表结构失败', error);
                this.$message.error('获取表结构失败');
              })
              .finally(() => {
                this.loading = false;
              });
          },

          generateEditRules() {
            this.editRules = {};
            this.tableFields.forEach(field => {
              if (field.Field !== 'id') {
                this.editRules[field.Field] = [{ required: true, message: `请输入${field.Field}`, trigger: 'blur' }];
              }
            });
          },

          fetchData() {
            if (!this.currentTable) return;
            this.loading = true;
            const params = {
                currentPage: this.currentPage,
                pageSize: this.pageSize,
                sortField: this.sortField,
                sortOrder: this.sortOrder
            };
            const data = this.searchQuery ? { search: this.searchQuery } : {};

            // 修改：使用新的API获取全部数据
            Promise.all([
                axios.post(`http://127.0.0.1:8080/${this.currentTable}/find`, data, { params }),
                axios.post(`http://127.0.0.1:8080/${this.currentTable}/findAll`, data, {
                    params: {
                        sortField: this.sortField,
                        sortOrder: this.sortOrder
                    }
                }),
                axios.get(`http://127.0.0.1:8080/${this.currentTable}/statistics`, { params: { search: this.searchQuery }})
            ])
            .then(responses => {
                this.tableData = responses[0].data.rows || [];
                this.totalCount = responses[0].data.totalCount || 0;

                // 设置全部股票数据
                this.allStockData = responses[1].data.rows || [];

                const stats = responses[2].data;
                if (stats.hasOwnProperty('record_count')) {
                    this.totalStockCount = stats.record_count;
                    this.risingStocks = 0;
                    this.fallingStocks = 0;
                    this.averagePrice = 0;
                } else {
                    this.totalStockCount = stats.total_stocks || 0;
                    this.risingStocks = stats.rising_stocks || 0;
                    this.fallingStocks = stats.falling_stocks || 0;
                    this.averagePrice = parseFloat(stats.average_price) || 0;
                }
                this.renderStockChart();
            })
            .catch(error => {
                console.error('获取数据失败', error);
                this.$message.error('数据加载失败');
                // 仅在API请求失败时使用模拟数据
                if (error.response && error.response.status >= 400) {
                  this.mockData();
                }
                this.renderStockChart();
            })
            .finally(() => {
                this.loading = false;
            });
          },

          mockData() {
            // 仅在API请求失败时使用模拟数据
            this.tableData = [
              { id: 1, code: "600000", name: "浦发银行", price: 7.89, price_change: 0.12, change_percentage: 1.54 },
              { id: 2, code: "600004", name: "白云机场", price: 13.52, price_change: -0.23, change_percentage: -1.67 },
              { id: 3, code: "600006", name: "东风汽车", price: 5.21, price_change: 0.05, change_percentage: 0.97 },
              { id: 4, code: "600007", name: "中国国贸", price: 18.45, price_change: -0.12, change_percentage: -0.65 },
              { id: 5, code: "600008", name: "首创环保", price: 2.97, price_change: 0.08, change_percentage: 2.76 },
              { id: 6, code: "600009", name: "上海机场", price: 49.87, price_change: 1.23, change_percentage: 2.53 },
              { id: 7, code: "600010", name: "包钢股份", price: 1.85, price_change: -0.03, change_percentage: -1.60 },
              { id: 8, code: "600011", name: "华能国际", price: 7.23, price_change: 0.15, change_percentage: 2.12 },
              { id: 9, code: "600012", name: "皖通高速", price: 7.56, price_change: -0.05, change_percentage: -0.66 },
              { id: 10, code: "600015", name: "华夏银行", price: 5.89, price_change: 0.08, change_percentage: 1.38 }
            ];
            this.allStockData = [...this.tableData]; // 模拟全部数据与分页数据相同
            this.totalCount = 500;
            this.totalStockCount = 500;
            this.risingStocks = 200;
            this.fallingStocks = 250;
            this.averagePrice = 12.56;
          },

          handleSelect(key) {
            this.activeIndex = key;
            const routeMap = {
              '1': '/stock',
              '2': '/next',
              '3': '/shenzhen',
              '4': '/american',
              '5': '/hongkong',
              '6': '/english',
              '7': '/tospider',
            };
            const route = routeMap[key];
            if (route) {
              window.location.href = route;
            }
          },

          searchData() {
            this.currentPage = 1;
            this.fetchData();
          },

          handleSelectionChange(rows) {
            this.selectedRows = rows;
          },

          showEditDialog(record = null) {
            if (record) {
                this.currentRecord = JSON.parse(JSON.stringify(record));
            } else {
                this.currentRecord = {};
                this.tableFields.forEach(field => {
                    if (field.Field !== 'id') {
                        this.currentRecord[field.Field] = '';
                    }
                });
            }
            this.editDialogVisible = true;
          },

          editRecord(row) {
            this.showEditDialog(row);
          },

          saveRecord() {
            this.$refs.editForm.validate(valid => {
                if (valid) {
                    const isEdit = this.currentRecord.id !== undefined;
                    const apiUrl = isEdit ?
                        `http://127.0.0.1:8080/${this.currentTable}/update?id=${this.currentRecord.id}` :
                        `http://127.0.0.1:8080/${this.currentTable}/add`;

                    axios.post(apiUrl, this.currentRecord)
                        .then(response => {
                            if (response.data.success) {
                                this.$message.success(isEdit ? '更新成功' : '添加成功');
                                this.editDialogVisible = false;
                                this.fetchData();
                            } else {
                                this.$message.error(response.data.message);
                            }
                        })
                        .catch(error => {
                            console.error('保存失败', error);
                            this.$message.error('保存失败，请重试');
                        });
                }
            });
          },

          deleteRecord(record) {
            this.$confirm('确定要删除这条记录吗？', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                axios.delete(`http://127.0.0.1:8080/${this.currentTable}/delete`, {
                    params: { id: record.id }
                })
                .then(response => {
                    if (response.data.success) {
                        this.fetchData();
                        this.$message.success('删除成功');
                    } else {
                        this.$message.error(response.data.message);
                    }
                })
                .catch(error => {
                    console.error('删除失败', error);
                    this.$message.error('删除失败，请重试');
                });
            }).catch(() => {});
          },

          deleteSelected() {
            if (!this.selectedRows.length) {
                this.$message.warning('请选择要删除的记录');
                return;
            }

            this.$confirm(`确定要删除选中的 ${this.selectedRows.length} 条记录吗？`, '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                const ids = this.selectedRows.map(row => row.id);
                axios.post(`http://127.0.0.1:8080/${this.currentTable}/delete`, ids)
                    .then(response => {
                        if (response.data.success) {
                            this.fetchData();
                            this.selectedRows = [];
                            this.$message.success(`成功删除 ${response.data.deleted_count} 条记录`);
                        } else {
                            this.$message.error(response.data.message);
                        }
                    })
                    .catch(error => {
                        console.error('批量删除失败', error);
                        this.$message.error('批量删除失败，请重试');
                    });
            }).catch(() => {});
          },

          formatPriceField(row, field) {
            if (!row[field] && row[field] !== 0) return '-';

            const value = parseFloat(row[field]);
            const isPercentage = field === 'change_percentage';
            const formattedValue = isPercentage ? value.toFixed(2) + '%' : value.toFixed(2);

            if (value > 0) {
              return `<span class="price-up">+${formattedValue}</span>`;
            } else if (value < 0) {
              return `<span class="price-down">${formattedValue}</span>`;
            } else {
              return formattedValue;
            }
          },

          handlePageSizeChange(size) {
            this.pageSize = size;
            this.fetchData();
          },

          handleCurrentPageChange(page) {
            this.currentPage = page;
            this.fetchData();
          },

          sortTable() {
            if (!this.sortField) {
                this.$message.warning('请输入排序字段');
                return;
            }

            const fieldExists = this.tableFields.some(field => field.Field === this.sortField);
            if (!fieldExists) {
                this.$message.error('排序字段不存在');
                return;
            }

            this.currentPage = 1;
            this.fetchData();
          },

          showStatsDialog() {
            if (!this.tableData.length) {
                this.$message.warning('没有数据可供统计');
                return;
            }

            const numericField = this.tableFields.find(
                f => /int|float|decimal/i.test(f.Type)
            );
            if (numericField) {
                this.statsField = numericField.Field;
            }

            this.statsDialogVisible = true;
          },

          getStatsTypeName(type) {
            const types = {
                'avg': '平均值',
                'median': '中位数',
                'mode': '众数',
                'range': '极差',
                'stddev': '标准差'
            };
            return types[type] || type;
          },

          calculateStats() {
            if (!this.statsField || !this.statsType) {
                this.$message.warning('请选择字段和统计类型');
                return;
            }

            // 使用全部数据进行统计
            const values = this.allStockData
                .map(row => parseFloat(row[this.statsField]))
                .filter(value => !isNaN(value));

            if (!values.length) {
                this.$message.warning('该字段没有可计算的数值数据');
                return;
            }

            let result;

            try {
                switch (this.statsType) {
                    case 'avg':
                        result = values.reduce((sum, val) => sum + val, 0) / values.length;
                        result = result.toFixed(2);
                        break;

                    case 'median':
                        values.sort((a, b) => a - b);
                        const mid = Math.floor(values.length / 2);
                        result = values.length % 2 !== 0 ? values[mid] : (values[mid - 1] + values[mid]) / 2;
                        result = result.toFixed(2);
                        break;

                    case 'mode':
                        const counts = {};
                        values.forEach(val => {
                            counts[val] = (counts[val] || 0) + 1;
                        });
                        let maxCount = 0;
                        let mode = values[0];
                        Object.keys(counts).forEach(key => {
                            if (counts[key] > maxCount) {
                                maxCount = counts[key];
                                mode = key;
                            }
                        });
                        result = mode;
                        break;

                    case 'range':
                        result = (Math.max(...values) - Math.min(...values)).toFixed(2);
                        break;

                    case 'stddev':
                        const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
                        const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
                        result = Math.sqrt(variance).toFixed(2);
                        break;

                    default:
                        this.$message.error('不支持的统计类型');
                        return;
                }

                this.statsResult = result;
            } catch (error) {
                console.error('统计计算失败', error);
                this.$message.error('统计计算失败');
            }
          },

          exportData() {
            if (!this.tableData.length) {
                this.$message.warning('没有数据可导出');
                return;
            }

            this.$confirm('确定要导出当前数据吗？', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                this.loading = true;
                const data = {
                    search: this.searchQuery,
                    sortField: this.sortField,
                    sortOrder: this.sortOrder
                };

                axios.post(`http://127.0.0.1:8080/${this.currentTable}/export`, data, {
                    responseType: 'blob'
                })
                .then(response => {
                    const url = window.URL.createObjectURL(new Blob([response.data]));
                    const link = document.createElement('a');
                    link.href = url;
                    link.setAttribute('download', `${this.currentTable}_${new Date().toISOString().slice(0, 10)}.xlsx`);
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    this.$message.success('导出成功');
                })
                .catch(error => {
                    console.error('导出失败', error);
                    this.$message.error('导出失败，请重试');
                })
                .finally(() => {
                    this.loading = false;
                });
            }).catch(() => {});
          },

          renderStockChart() {
            const chartDom = document.getElementById('stockChart');
            if (!chartDom) return;

            const myChart = echarts.init(chartDom);

            // 使用全部数据绘制图表
            const data = this.allStockData;
            if (!data || data.length === 0) {
                myChart.setOption({
                    title: {
                        text: '暂无数据',
                        left: 'center'
                    }
                });
                return;
            }

            // 根据涨跌幅分组
            const bins = [-Infinity, -5, -3, -1, 0, 1, 3, 5, Infinity];
            const binLabels = ['<-5%', '-5%~-3%', '-3%~-1%', '-1%~0%', '0%~1%', '1%~3%', '3%~5%', '>5%'];
            const binCounts = Array(bins.length - 1).fill(0);

            // 计算每组数量
            data.forEach(item => {
                const change = parseFloat(item.change_percentage || 0);
                for (let i = 0; i < bins.length - 1; i++) {
                    if (change > bins[i] && change <= bins[i + 1]) {
                        binCounts[i]++;
                        break;
                    }
                }
            });

            // 设置图表颜色
            const colors = binLabels.map(label => {
                if (label.includes('-')) return '#2ecc71'; // 下跌绿色
                if (label.includes('+') || label.includes('>')) return '#e74c3c'; // 上涨红色
                return '#999'; // 持平灰色
            });

            // 图表配置
            const option = {
                backgroundColor: '#fff',
                color: colors,
                title: {
                    text: '股票涨跌幅分布',
                    left: 'center',
                    textStyle: {
                        color: '#333',
                        fontWeight: 'bold',
                        fontSize: 16
                    }
                },
                tooltip: {
                    trigger: 'axis',
                    axisPointer: {
                        type: 'shadow'
                    }
                },
                grid: {
                    left: '3%',
                    right: '4%',
                    bottom: '3%',
                    top: '15%',
                    containLabel: true
                },
                xAxis: {
                    type: 'category',
                    data: binLabels,
                    axisTick: {
                        alignWithLabel: true
                    },
                    axisLabel: {
                        rotate: 45,
                        interval: 0
                    }
                },
                yAxis: {
                    type: 'value',
                    name: '股票数量',
                    axisLabel: {
                        formatter: '{value}'
                    }
                },
                series: [{
                    name: '股票数量',
                    type: 'bar',
                    data: binCounts,
                    label: {
                        show: true,
                        position: 'top'
                    },
                    itemStyle: {
                        borderRadius: 5
                    }
                }]
            };

            // 使用配置项显示图表
            myChart.setOption(option);

            // 监听窗口大小变化，调整图表
            window.addEventListener('resize', () => {
                myChart.resize();
            });
          }
        }
      });
    });
  </script>
</body>
</html>